<?php
///////////////////////////////////////////////////////////////////////////////////////////////////////
//  这个文件是 JCAT PHP框架的一部，该项目和此文件 均遵循 GNU 自由软件协议
// 
//  Copyleft 2008 JeCat.cn(http://team.JeCat.cn)
//
//
//  JCAT PHP框架 的正式全名是：Jellicle Cat PHP Framework。
//  “Jellicle Cat”出自 Andrew Lloyd Webber的音乐剧《猫》（《Prologue:Jellicle Songs for Jellicle Cats》）。
//  JCAT 是一个开源项目，它像音乐剧中的猫一样自由，你可以毫无顾忌地使用JCAT PHP框架。JCAT 由中国团队开发维护。
//  正在使用的这个版本是：0.5.0 / SVN信息: $Id: class.JCAT_FSPackage.php 1932 2009-07-09 18:17:33Z alee $
//
//
//
//  相关的链接：
//    [主页] http://jcat.JeCat.cn
//    [下载(HTTP)] http://code.google.com/p/jcat-php/downloads/list
//    [下载(svn)] svn checkout http://jcat-php.googlecode.com/svn/branches/0.4.0/Framework/ JCAT0.4
//    [在线文档] http://jcat.JeCat.cn/document
//    [社区] http://jj.jecat.cn/forum-7-1.html
//  不很相关：
//    [MP3] http://www.google.com/search?q=jellicle+songs+for+jellicle+cats+Andrew+Lloyd+Webber
//    [VCD/DVD] http://www.google.com/search?q=CAT+Andrew+Lloyd+Webber+video
//
///////////////////////////////////////////////////////////////////////////////////////////////////////
/*-- Project Introduce --*/

/**
 * 用于文件系统打包
 *
 */
class JCAT_FSPackage extends JCAT_FSPackageOperatorBase
{
	const PACKAGE_SYMBOL = 'JCAT.PACKAGE' ;
	const PACKAGE_SYMBOL_LEN = 12 ;
	const PACKAGE_HEADER_LEN = 21 ;
	const PACKAGE_VERSION = '0.2' ;
	const PACKAGE_VERSION_INT32 = 8388608 ;

	// 释放方式
	const RELEASE_OVERWRITE = 1 ;		// 01 
	const RELEASE_VERIFY = 2 ;			// 10 
	
	const RELEASE_DEFAULT = 1 ;			// RELEASE_OVERWRITE
	
	// 迭代方式
	const ITER_DIR = 1 ;
	const ITER_FILE = 2 ;
	const ITER_ALL = 3 ;
	const ITER_RET_FILENAME = 4 ;
	const ITER_RET_DIR = 8 ;
	const ITER_RET_PATH = 12 ;
	const ITER_RET_FSO = 16 ;
	
	const ITER_DEFAULT = 15 ;		// ITER_ALL|ITER_RET_PATH
	
	
	/**
	 * Description
	 *
	 * @access	public
	 * @return	void
	 */
	public function JCAT_FSPackage($sPackagePath)
	{
		$this->JCAT_FSPackageOperatorBase() ;		
		$this->LoadPackage($sPackagePath) ;
	}
	
	/**
	 * Description
	 *
	 * @access	private
	 * @param	$sPackagePath	string
	 * @return	JCAT_FSPackage
	 */
	private function LoadPackage($sPackagePath)
	{
		// 创建/设置 文件包对象
		$this->sPackagePath = $sPackagePath ;
		
		// 读取包文件 
		$hPackage = fopen($sPackagePath,'rb') ;
		if(!$hPackage)
		{
			throw new JCAT_Exception("无法读取文件包：".$sPackagePath) ;
		}
		
		// 读取 Header 信息
		$sHeader = fread($hPackage,self::PACKAGE_HEADER_LEN) ;
		$arrHeaders = self::ReadPackageHeader($sHeader) ;
		if($arrHeaders===false)
		{
			throw new JCAT_Exception("无效的文件包（文件格式无效）：".$sPackagePath) ;
		}
		
		$bLstCompressed = $arrHeaders['bLstCompressed'] ;
		$nFileLstLen = $arrHeaders['nLstLen'] ;
		
		// 包内文件开始偏移位置
		$this->nFileOffice = self::PACKAGE_HEADER_LEN+$nFileLstLen ;
		

		// 读取 文件包信息xml
		$sPackInfoXML = fread($hPackage,$nFileLstLen) ;
		
		// 解压缩文件包信息xml
		if($bLstCompressed)
		{
			// 检查 zlib
			JCAT_FSPackageOperatorBase::CheckZLib() ;
			
			$sPackInfoXML = gzuncompress($sPackInfoXML) ;
			if(!$sPackInfoXML)
			{
				throw new JCAT_Exception("解压文件包的信息时出错：".$sPackagePath) ;
			}
		}
		
		$aPackInfoDoc = new SimpleXMLElement($sPackInfoXML) ;
		
		// 读取 文件清单 xml 
		$arrFileLst = $aPackInfoDoc->xpath('FileLst') ;
		if(count($arrFileLst))
		{
			$this->aRootFolder->LoadFromXMLDoc($arrFileLst[0]) ;
		}
		
		// 读取文件包的扩展信息
		$arrExtensionalItems = $aPackInfoDoc->xpath('Extensional/Item') ;
		foreach($arrExtensionalItems as $aExtensionalItem)
		{
			$this->SetExtensionalInfo(strval($aExtensionalItem['name']),strval($aExtensionalItem));
		}
		
		// 关闭文件包/返回文件包对象
		fclose($hPackage) ;
	}
	
	/**
	 * Description
	 *
	 * @access	public
	 * @param	$sHeaderStream
	 * @static
	 * @return	array
	 */
	static public function ReadPackageHeader($sHeaderStream)
	{
		if( strlen($sHeaderStream)!==(self::PACKAGE_HEADER_LEN) )
		{
			return false ;
		}
		
		$sHeaderFormat = '' ;
		for($nCharIdx=0,$sMark='a';$nCharIdx<self::PACKAGE_SYMBOL_LEN;$nCharIdx++,$sMark++)
		{
			$sHeaderFormat.= "C{$sMark}/" ;
		}
		$sHeaderFormat.= 'lnVer/CbLstCompressed/lnLstLen' ;
		
		$arrHeaderInfos = unpack($sHeaderFormat,$sHeaderStream) ;
		if( !is_array($arrHeaderInfos)
				or !isset($arrHeaderInfos['nVer'])
				or !isset($arrHeaderInfos['bLstCompressed'])
				or !isset($arrHeaderInfos['nLstLen'])
				or count($arrHeaderInfos)!=(self::PACKAGE_SYMBOL_LEN+1+1+1) )
		{
			return false ;
		}
		
		// 检查符号
		$sMustMark = 'a' ;
		$arrSymbols = array_slice($arrHeaderInfos,0,self::PACKAGE_SYMBOL_LEN) ;
		foreach ($arrSymbols as $sMark=>&$sSymbol)
		{
			if($sMustMark!=$sMark)
			{
				return false ;
			}
			$sMustMark ++ ;
			
			$sSymbol = chr(255-$sSymbol) ;
		}
		$sSymbol = implode('',$arrSymbols) ;
		
		if($sSymbol!=self::PACKAGE_SYMBOL)
		{
			return false ;
		}
		
		if($arrHeaderInfos['nVer']!=self::PACKAGE_VERSION_INT32)
		{
			return false ;
		}
		
		$arrHeaderInfos['nLstLen'] = intval($arrHeaderInfos['nLstLen']) ;
		$arrHeaderInfos['bLstCompressed'] = $arrHeaderInfos['bLstCompressed']=='0'? false: true ;
		
		return $arrHeaderInfos ;
	}
	
	
	/**
	 * Description
	 *
	 * @access	public
	 * @param	$sPackagePath	string
	 * @static
	 * @return	bool
	 */
	static public function VerifyPackageHeader($sPackagePath)
	{
		// 读取包文件 
		$hPackage = fopen($sPackagePath,'rb') ;
		if(!$hPackage)
		{
			return false ;
		}
		
		// 读取 Header 信息
		$sHeader = fread($hPackage,self::PACKAGE_HEADER_LEN) ;
		fclose($hPackage) ;
		
		return self::ReadPackageHeader($sHeader)!==false ;
	}


	
	/**
	 * 从文件包里释放文件
	 *
	 * @access	public
	 * @param	$Parameter
	 * @return	void
	 */
	public function Release($sToFolder,$sPathInPkg='/',$nReleaseFlg=JCAT_FSPackage::RELEASE_DEFAULT)
	{		
		if( !file_exists($sToFolder) )
		{
			if( mkdir($sToFolder) )
			{
				chmod($sToFolder,0777) ;
			}
		}
		
		if( !is_dir($sToFolder) or !is_writable($sToFolder) )
		{
			throw new JCAT_Exception("目录不存在（也无法创建）或缺少可写入权限：".$sToFolder) ;
		}
		
		// 
		$aFSO = ($sPathInPkg=='/')? 
				$this->aRootFolder: $this->aRootFolder->GetChild(JCAT_FSPackageOperatorBase::MakePkgPath($sPathInPkg)) ;
		
		$hPackage = fopen($this->sPackagePath,'rb') ; 
		$aFSO->Release($sToFolder,$hPackage,$this->nFileOffice,$nReleaseFlg) ;
		
		fclose($hPackage) ;
	}
	

	
	//////////////////////////////////////////////////////////////////////////////////////////
	
	/**
	 * Description
	 * 
	 * @access	private
	 * @var		string
	 */
	private $sPackagePath ;
	
	/**
	 * Description
	 * 
	 * @access	private
	 * @var		int
	 */
	private $nFileOffice = 0 ;
	
}

?>